#pragma once

#include <cassert>
#include <cstdint>
#include <cstring>

#define CYB_MEMORY_ALIGNMENT			(size_t)(2 * sizeof(void *))
#define CYB_CHUNK_ALIGN_MASK			(CYB_MEMORY_ALIGNMENT - (size_t) 1)
#define CYB_IS_ALIGNED( A )				((((size_t)((A)) & (CYB_CHUNK_ALIGN_MASK)) == 0)
#define CYB_ALIGN_OFFSET( A )			((((size_t)(A) & CYB_CHUNK_ALIGN_MASK) == 0) ? 0 : ((CYB_MEMORY_ALIGNMENT - ((size_t)(A) & CYB_CHUNK_ALIGN_MASK)) & CYB_CHUNK_ALIGN_MASK))

namespace cyb
{

class LinearAllocator 
{
public:
	LinearAllocator() = delete;
	LinearAllocator( const LinearAllocator &rhs ) = delete;
	LinearAllocator &operator=( const LinearAllocator &rhs ) = delete;

	explicit LinearAllocator( size_t size )
	{
		assert( size != 0 );
		m_size = size;
		m_start = new uint8_t[m_size];
		Reset();
	}

	~LinearAllocator() 
	{
		delete[] m_start;
	}

	void *Allocate( size_t size ) 
	{
		assert( size != 0 );
		size_t adjustment = CYB_ALIGN_OFFSET( m_current );
		assert( ( m_current + size + adjustment ) <= m_start + m_size && "Out of memory" );

		uint8_t *alignedMemory = m_current + adjustment;
		m_current = alignedMemory + size;
		m_allocations++;

		return static_cast<void *>( alignedMemory );
	}

	void Clear()
	{
		assert( m_allocations == 0 && "Clearing non-private memory" );
		memset( m_start, 0, m_size );
	}

	void Reset()
	{
		m_current = m_start;
		m_allocations = 0;
	}

private:
	uint8_t *m_start;
	uint8_t *m_current;
	size_t m_size;
	uint32_t m_allocations;
};

}	// namespace cyb